#pragma once

#include <windef.h>
#include <hooks/MouseHook.hpp>
#include <hooks/KeyboardHook.hpp>
#include <logger/stream.hpp>
#include <engine.core/math.hpp>
#include <engine.core/Info.hpp>

#include "Device.hpp"
#include "Win32Key.hpp"




namespace Engine
{
	namespace Input
	{
		class Win32Device
			: public Device
		{
		public:

			typedef boost::shared_ptr<Win32Device>             ptr;
			typedef std::pair<const Key::Type,Win32Key>        KeyPair;
			typedef boost::unordered_map<Key::Type,Win32Key>   Keys;

		public:

			Win32Device(const EngineInfo &info);
			~Win32Device();

			/**
			 * Reports the name of the underlying API this Device uses.
			 */
			QString apiName() const;

			void update();
			void resize(int width, int height);
			void changeWindow(HWND window);

			int2   mousePositionPixels() const;
			int2   mouseDeltaPixels() const;

			float2 mousePositionRelative() const;
			float2 mouseDeltaRelative() const;

			float mouseWheelDelta() const;

			const Key& key(Key::Type k) const;
			bool keyPressed(Key::Type k) const;
			bool keyClicked(Key::Type k) const;
			bool keyDown(Key::Type k) const;

		private:

			/**
			 * Get the key state of a particular key
			 * in order to modify it.
			 */
			Win32Key& win32Key(Key::Type key);

			/**
			 * Creates entries for all keys of the keyboard/mouse.
			 */
			void setupPhysicalKeys();

			/**
			 * Function that processes intercepted mouse messages.
			 */
			void mouseProc(WPARAM wParam, LPARAM lParam);

			/**
			 * Function that processes intercepted keyboard messages.
			 */
			void keyboardProc(int message, WPARAM wParam, LPARAM lParam);

		private:

			logger::stream::ptr     m_logger;
			HWND                    m_window;
			MouseHook               m_mouseHook;
			KeyboardHook            m_keyboardHook;

			int2                    m_size;


			/// Most current mouse position (it may change several times
			/// per frame, as it is retrieved by the mouse hook, that intercepts
			/// WM_MOUSE messages).
			int2                    m_currentMousePositionPixels;

			/// Mouse position for the current frame in pixels.
			/// Relative to the upper left corner.
			int2                    m_mousePositionPixels;

			/// Change of the mouse position since the last frame
			/// in pixels.
			int2                    m_mouseDeltaPixels;

			/// Mouse position for the current frame.
			/// In screen coordinates (window size independent),
			/// relative to the upper left corner.
			float2                  m_mousePositionRelative;

			/// Mouse Delta defines the delta between the mouse positions of 2 frames
			float2                  m_mouseDeltaRelative;

			/// Mouse Wheel Data is an intermediate variable that is set by
			/// the mouseProc function
			float                   m_currentMouseWheelDelta;
			float                   m_mouseWheelDelta;

			Keys                    m_keys;
		};
		///////////////////////////////////////////////////////////////////////////////////////////
	}
	///////////////////////////////////////////////////////////////////////////////////////////////
}
///////////////////////////////////////////////////////////////////////////////////////////////////
